package com.github.baichuan.application.scaffold.rest.domain.identity;

import com.github.baichuan.application.scaffold.rest.domain.organization.Org;
import com.github.baichuan.application.scaffold.rest.domain.organization.OrgService;
import com.github.baichuan.application.scaffold.rest.jpa.authorization.RoleRepository;
import com.github.baichuan.application.scaffold.rest.jpa.identity.ApplicationMemberRepository;
import com.github.baichuan.application.scaffold.rest.jpa.identity.MemberLoginRepository;
import com.github.baichuan.application.scaffold.rest.jpa.identity.MemberRepository;
import com.github.baichuan.application.scaffold.rest.dto.membership.ChangePassword;
import com.github.baichuan.application.scaffold.rest.dto.membership.EditForm;
import com.github.baichuan.application.scaffold.rest.dto.membership.RegisterForm;
import com.github.baichuan.application.scaffold.rest.dto.membership.ResetPassword;
import com.github.baichuan.application.scaffold.rest.security.PasswordSecurityPolicy;
import com.github.baichuan.application.scaffold.rest.security.PasswordSecurityProvider;
import com.github.baichuan.web.dto.Result;
import com.github.baichuan.web.error.NoSuchEntityException;
import com.github.baichuan.application.scaffold.rest.domain.application.Application;
import com.github.baichuan.application.scaffold.rest.domain.application.ApplicationService;
import com.github.baichuan.application.scaffold.rest.domain.application.Realm;
import com.github.baichuan.application.scaffold.rest.domain.authorization.Role;
import com.github.baichuan.web.security.PasswordEncryption;
import com.github.baichuan.utils.SnowFlake;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.LinkedList;
import java.util.List;
import java.util.Optional;

@Slf4j
@Service
@Transactional(rollbackFor = Exception.class)
public class ApplicationMemberService {
    @Autowired
    public ApplicationMemberService(MemberRepository memberRepository,
                                    ApplicationMemberRepository applicationMemberRepository,
                                    MemberLoginRepository loginRepository,
                                    ApplicationService applicationService,
                                    OrgService orgService,
                                    RoleRepository roleRepository,
                                    PasswordEncryption passwordEncryption,
                                    PasswordSecurityProvider passwordSecurityProvider,
                                    SnowFlake snowFlake) {
        this.memberRepository = memberRepository;
        this.applicationMemberRepository = applicationMemberRepository;
        this.loginRepository = loginRepository;
        this.applicationService = applicationService;
        this.orgService = orgService;
        this.roleRepository = roleRepository;
        this.passwordEncryption = passwordEncryption;
        this.passwordSecurityProvider = passwordSecurityProvider;
        this.snowFlake = snowFlake;
    }

    private MemberRepository memberRepository;
    private ApplicationMemberRepository applicationMemberRepository;
    private MemberLoginRepository loginRepository;
    private ApplicationService applicationService;
    private OrgService orgService;
    private RoleRepository roleRepository;
    private PasswordEncryption passwordEncryption;
    private SnowFlake snowFlake;
    private PasswordSecurityProvider passwordSecurityProvider;

    public ApplicationMember find(String id) {
        return applicationMemberRepository.findById(id).orElseThrow(() -> new NoSuchEntityException("用户不存在"));
    }

    public ApplicationMember find(String application, String id) {
        return applicationMemberRepository.find(application, id).orElseThrow(() -> new NoSuchEntityException("用户不存在"));
    }

    /**
     * 根据钉钉UnionId获取用户信息
     */
    public Optional<ApplicationMember> findByDingUser(String application, String unionId){
        return applicationMemberRepository.findByDingUser(application, unionId);
    }

    public Page<ApplicationMember> query(Specification<ApplicationMember> specification, Pageable pageable) {
        return applicationMemberRepository.findAll(specification, pageable);
    }

//    public List<ApplicationMember> suggest(String applicationCode, String keyword){
//        Application application = applicationService.find(applicationCode);
//        return applicationMemberRepository.suggest(application, keyword.trim());
//    }

    public Result add(String applicationCode, RegisterForm form) {
        Application application = applicationService.findByCode(applicationCode);
        PasswordSecurityPolicy passwordSecurity = passwordSecurityProvider.get(application);
        Result strengthVerify = passwordSecurity.strength(form.getPassword());
        if(!strengthVerify.isSuccess()){
            return strengthVerify;
        }

        String loginName = form.getLoginName().trim();
        Optional<Member> duplicationLoginName = memberRepository.find(application.getRealm(), loginName);
        Member member = null;
        if (duplicationLoginName.isPresent()) {
            Member existMember = duplicationLoginName.get();
            String cipherPassword = encrypt(form.getPassword().trim(), existMember.getSalt());
            if(!existMember.getPassword().equals(cipherPassword)){
                return Result.fail("登录名已被注册");
            }
            member = existMember;
        } else {
            member = createMember(application.getRealm(), form, loginName);
            memberRepository.save(member);
        }

        List<Role> roles = new LinkedList<>();
        if (form.getRoles() != null && form.getRoles().size() > 0) {
            roles = roleRepository.findByIdIn(form.getRoles());
            if (roles.stream().anyMatch(role -> !role.getApplication().getCode().equals(application.getCode()))) {
                return Result.fail("角色的平台编码不匹配");
            }
        }

        ApplicationMember applicationMember = ApplicationMember.newInstance(application, member, snowFlake.nextBase62Id());
        if (StringUtils.isNotBlank(form.getOrgUid())) {
            applicationMember.setOrg(orgService.find(application, form.getOrgUid()));
        }
        applicationMember.setMemberType(form.getMemberType().trim());
        applicationMember.setExpire(LocalDate.parse(form.getExpire().trim(), DateTimeFormatter.ISO_LOCAL_DATE));
        if (roles.size() > 0) {
            applicationMember.setRoles(roles);
        }

        applicationMemberRepository.save(applicationMember);
        return Result.success("用户注册成功", applicationMember.getId());
    }

    public Result edit(String applicationCode, String id, EditForm form) {
        ApplicationMember applicationMember = find(applicationCode, id);
        Member member = applicationMember.getMember();
        Application application = applicationMember.getApplication();

        if (form.getRoles() != null && form.getRoles().size() > 0) {
            List<Role> roles = roleRepository.findByIdIn(form.getRoles());
            if (!roles.stream().allMatch(role -> role.getApplication().getCode().equals(application.getCode()))) {
                return Result.fail("角色的平台编码不匹配");
            }
        }

        member.setName(form.getName().trim());

        if(StringUtils.isNotBlank(form.getEmail())){
            member.setEmail(form.getEmail());
        }
        if(StringUtils.isNotBlank(form.getMobile())){
            member.setMobile(form.getMobile());
        }
        if (StringUtils.isNotBlank(form.getOrgUid())) {
            Org org = orgService.find(application, form.getOrgUid());
            applicationMember.setOrg(org);
        } else {
            applicationMember.setOrg(null);
        }
        applicationMember.setMemberType(form.getMemberType());
        applicationMember.setExpire(LocalDate.parse(form.getExpire(), DateTimeFormatter.ISO_LOCAL_DATE));

        //添加角色
        for (String roleId : form.getRoles()) {
            if (applicationMember.getRoles().stream().noneMatch(role -> role.getId().equals(roleId))) {
                Optional<Role> optionalRole = roleRepository.find(application, roleId);
                if (optionalRole.isPresent() && optionalRole.get().getApplication() == application) {
                    applicationMember.getRoles().add(optionalRole.get());
                }
            }
        }

        //移除角色
        for (int i = applicationMember.getRoles().size() - 1; i >= 0; i--) {
            Role role = applicationMember.getRoles().get(i);
            if (form.getRoles().stream().noneMatch(x -> x == role.getId())) {
                applicationMember.getRoles().remove(role);
            }
        }

        memberRepository.save(member);
        applicationMemberRepository.save(applicationMember);
        return Result.success("修改成功");
    }

    public Result remove(String application, String uid){
        ApplicationMember applicationMember = find(application, uid);
        applicationMemberRepository.delete(applicationMember);

        Member member = applicationMember.getMember();
        loginRepository.deleteByMember(member);

        if (applicationMemberRepository.findByMember(member).isEmpty()){
            memberRepository.delete(member);
        }
        return Result.success("删除成功");
    }

    public ApplicationMember save(ApplicationMember applicationMember){
        return applicationMemberRepository.save(applicationMember);
    }

    /**
     * 获取用户被授权的资源代码
     *
     * @param member
     * @return
     */
    public List<String> getAuthorizationCodes(ApplicationMember member) {
        return applicationMemberRepository.getAuthorizationCodes(member.getId());
    }

    public Result resetPassword(String application, ResetPassword reset) {
        if(!reset.getPassword().equals(reset.getConfirmPassword())){
            return Result.fail("密码与确认密码不一致");
        }

        ApplicationMember applicationMember = find(application, reset.getUid());

        PasswordSecurityPolicy passwordSecurity = passwordSecurityProvider.get(applicationMember.getApplication());
        Result strengthVerify = passwordSecurity.strength(reset.getPassword());
        if(!strengthVerify.isSuccess()){
            return strengthVerify;
        }

        Member member = applicationMember.getMember();
        member.setPassword(encrypt(reset.getPassword(), member.getSalt()));
        memberRepository.save(member);
        return Result.success("重置密码成功");
    }

    public Result changePassword(String application, ChangePassword change) {
        String oldPassword = change.getOldPassword().trim();
        String newPassword = change.getNewPassword().trim();
        if (oldPassword.equals(newPassword)) {
            return Result.fail("新密码与旧密码不能重复");
        }

        ApplicationMember applicationMember = find(application, change.getUid());

        PasswordSecurityPolicy passwordSecurity = passwordSecurityProvider.get(applicationMember.getApplication());
        Result strengthVerify = passwordSecurity.strength(change.getNewPassword());
        if(!strengthVerify.isSuccess()){
            return strengthVerify;
        }

        Member member = applicationMember.getMember();
        String oldCipher = encrypt(oldPassword, member.getSalt());
        if (!oldCipher.equals(member.getPassword())) {
            return Result.fail("当前密码错误");
        }

        String newCipher = encrypt(newPassword, member.getSalt());
        member.setPassword(newCipher);
        memberRepository.save(member);
        return Result.success("密码修改成功！");
    }



    public Page<MemberLogin> signinLogs(Specification<MemberLogin> specification, Pageable pageable) {
        return loginRepository.findAll(specification, pageable);
    }

    public String encrypt(String password, String salt){
        return passwordEncryption.digest(password, salt);
    }

    private Member createMember(Realm realm, RegisterForm form, String loginName) {
        Member member = new Member();
        member.setId(snowFlake.nextBase62Id());
        member.setRealm(realm);
        member.setLoginName(loginName);
        member.setName(form.getName().trim());
        if (StringUtils.isNotBlank(form.getEmail())) {
            member.setEmail(form.getEmail().trim());
        }
        if (StringUtils.isNotBlank(form.getMobile())) {
            member.setMobile(form.getMobile().trim());
        }
        member.setSalt(snowFlake.nextBase62Id());
        member.setPassword(encrypt(form.getPassword().trim(), member.getSalt()));
        member.setCreateTime(LocalDateTime.now());
        member.setPasswordUpdateDate(LocalDate.now());
        return member;
    }
}
